#进程池
'''
主要的方法：
p.apply_async(func[,args[,kwds]])非阻塞方式调用func （并行）
func 函数名
args 为传递个func的参数列表--元组形式
kwds 为传递给func的关键字参数列表


p.close() 关闭进程池，防止进一步操作（进程池不接受新任务）
p.join()  阻塞
enumerate() 不管任务是否完成，立即终止

如果使用异步提交任务，等进程池内任务都处理完，需要用get()
来收集结果。

使用场景：
利用python进行系统管理的时候，同时操作多个文件目录、或者
远程控制多台主机，并行操作可以节约大量的时间。

'''

#同步和异步
'''
阻塞：遇到I/O就发生阻塞，程序一旦遇到阻塞操作就停在原地，
并立刻释放CPU的资源。

非阻塞：没有I/O操作或者通过某种手段让程序即便遇到IO操作，也
不会停在原地，而去执行其他操作，力求尽可能多的占有CPU资源。



同步与异步：（提交任务的两种方式）
同步调用：提交完任务后，就地等待，直到任务运行完毕后，拿到任务
的返回值，才继续执行下一行代码。

异步调用：提交完任务后，不在原地等待，直接执行下一行代码。

同步：我等你（当你告诉我（拿到任务的返回值）我已经执行完，那我再往下执行）

异步：只管提交任务执行（系统会通知任务是否执行完毕）
'''


#异步:不用等待当前进程，随时根据系统调度来进行进程切换。
import os
import time
from multiprocessing import Pool

def learn(n):
    print('我们在做学术交流')
    time.sleep(2)
    return n**2

if __name__=='__main__':
    #创建进程池，最大进程数为3
    p = Pool(3)
    list1 = []
    for i in range(6):
        #apply_async异步调用
        result = p.apply_async(learn,args=(i,)) #i为函数learn的参数
        #把结果添加到list1列表里
        list1.append(result)

    #关闭进程池,关闭后p进程池不在接受任务
    p.close()
    p.join()  #等待p中所有子进程执行完毕，必须放在close语句之后
    for j in list1:
        #使用get来获取apply_async的结果
        print(j.get())
print('========================================================')
print('\n')


#同步：apply  同步阻塞，等待当前子进程执行完毕后，再执行下一个进程
import os
import time
from multiprocessing import Pool

def learn(n):
    print('我们在做学术交流')
    time.sleep(2)
    return n**2

if __name__=='__main__':
    p = Pool(3)
    list1 = []
    for i in range(6):
       result = p.apply_async(learn,args=(i,))
       list1.append(result)
    print(list1)
print('========================================================')
print('\n')


#进程池间的通信
'''
Pool创建进程池，需要使用multiprocessing.Manager()中Queue()
进程池的通信：if multiprocessing.Queue()
Manager()模块，专门做数据共享，支持类型很多
如：value, array, list, dict,Queue, Lock等

multiprocessing模块下的Queue为进程提供服务；
queue模块下的Queue为线程提供服务。

队列实例化 q = Manager().Queue()
'''
from multiprocessing import Pool,Manager
import os
def rd(q):
    print(f'rd启动{os.getpid()},父进程{os.getppid}')
    for i in range(q.qsize()):  #q.qsize()返回队列中数据数量
        print('取出数据',q.get())

def wd(q):
    print(f'wd启动{os.getpid()},父进程{os.getppid}')
    for i in '123':
        print('wd中的：',i)
        q.put(i)        #把字符串123中的某个数据放入队列中

if __name__=='__main__':
    print('开始了',os.getpid())
    #实例化一个队列对象
    q = Manager().Queue()
    #创建进程池
    p = Pool()
    #异步
    p.apply_async(wd,args=(q,))
    p.apply_async(rd,args=(q,))
    p.close()
    p.join()
    print('结束了',os.getpid())
